import sun.awt.image.ImageWatched;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {
    // 服务端的 socket
    private ServerSocket serverSocket = null;

    public  TcpEchoServer(int port) throws IOException {
        // 指定一个端口绑定
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!!");
        // 线程池
        ExecutorService pool = Executors.newCachedThreadPool();
        while (true) {
            System.out.println("等待客户端建立 TCP 连接...");
            // accept, 有客户端尝试连接后, 与该客户端连接, 并且返回一个 Socket 对象, 没有则阻塞等待
            Socket clientSocket = serverSocket.accept();

            // 客户端连接后, 处理这个连接
            // 如果直接调用 processConnection 方法处理连接
            // 那么只能给一个客户端提供服务, 因为这个方法里是循环是处理客户端的请求
            // 需要给每个连接上的客户端创建一个线程, 一个线程处理一个客户端连接
            pool.submit(() -> {
                try {
                    processConnection(clientSocket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            /*
            Thread t = new Thread(() -> {
                try {
                    processConnection(clientSocket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            t.start();*/
        }
    }

    // 处理客户端连接
    // 一个连接有一个或多个请求
    // 读取请求, 计算响应, 返回给客户端
    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 客户端上线!!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());

        // try 的 () 中可以包含多个流对象, 用 ; 隔开
        // 通过 客户端的 Socket 对象, 构造输入输出流
        // 就可以读取客户端请求, 并且返回响应了
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            // 将字节流转为字符流
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);

            // 判断接下来还有没有数据
            while(scanner.hasNext()) {
                // 不要用 nextLine, 有坑
                // next 一直读, 读到空白符为止, 空格, 换行, 制表符, 翻页符
                // next 读一行, 读到换行符结束

                // 读取请求
                String request = scanner.next();
                // 根据请求计算响应
                String response = process(request);

                // 把请求写回给客户端
                printWriter.println(response);
                // 这里是写到网卡里的, 大多数网卡读写速度比硬盘慢.
                // 为了提高 IO 效率, 使用缓冲区, 来减少 IO 次数, 提高整体效率.
                // 把数据先放到内存的缓冲区中, 等缓冲区满了, 再统一写入网卡.
                // 需要通过 flush 刷新, 才能及时发送数据
                printWriter.flush();

                System.out.printf("[%s:%d] req: %s resp: %s\n", clientSocket.getInetAddress(), clientSocket.getPort(),
                        request, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            System.out.printf("[%s:%d] 客户端下线!!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
            // 客户端断开连接后, 就可以释放这个资源了
            clientSocket.close();
        }
    }

    // 根据请求计算响应
    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer tcpEchoServer = new TcpEchoServer(6666);
        tcpEchoServer.start();
    }
}
